// Copyright 2023 NetApp, Inc. All Rights Reserved.

package crd

import (
	"context"
	"io"
	"os"
	"testing"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/discovery"
	discoveryfake "k8s.io/client-go/discovery/fake"
	clientgotesting "k8s.io/client-go/testing"

	. "github.com/netapp/trident/logging"
	"github.com/netapp/trident/persistent_store/crd/client/clientset/versioned"
	"github.com/netapp/trident/persistent_store/crd/client/clientset/versioned/fake"
	v1 "github.com/netapp/trident/persistent_store/crd/client/clientset/versioned/typed/netapp/v1"
	v1fake "github.com/netapp/trident/persistent_store/crd/client/clientset/versioned/typed/netapp/v1/fake"
	tridenttesting "github.com/netapp/trident/testing"
)

var (
	crdScheme = runtime.NewScheme()
	codecs    = serializer.NewCodecFactory(crdScheme)
	ctx       = context.Background
)

func init() {
	utilruntime.Must(fake.AddToScheme(crdScheme))
}

func TestMain(m *testing.M) {
	// Disable any standard log output
	InitLogOutput(io.Discard)
	_ = InitLogLevel("Trace")
	os.Exit(m.Run())
}

// NewFakeClientset is a better replacement for the autogenerated fake.NewSimpleClientset that
// uses our own improved testing fixtures.
func NewFakeClientset(objects ...runtime.Object) *Clientset {
	o := tridenttesting.NewObjectTracker(crdScheme, codecs.UniversalDecoder())
	for _, obj := range objects {
		if err := o.Add(obj); err != nil {
			panic(err)
		}
	}

	cs := &Clientset{tracker: o}
	cs.discovery = &discoveryfake.FakeDiscovery{Fake: &cs.Fake}
	cs.AddReactor("*", "*", tridenttesting.ObjectReaction(o))
	cs.AddWatchReactor("*", func(action clientgotesting.Action) (handled bool, ret watch.Interface, err error) {
		gvr := action.GetResource()
		ns := action.GetNamespace()
		watchIntf, err := o.Watch(gvr, ns)
		if err != nil {
			return false, nil, err
		}
		return true, watchIntf, nil
	})

	return cs
}

// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.  Uses Trident's improved ObjectTracker.
type Clientset struct {
	clientgotesting.Fake
	discovery *discoveryfake.FakeDiscovery
	tracker   tridenttesting.ObjectTracker
}

func (c *Clientset) Discovery() discovery.DiscoveryInterface {
	return c.discovery
}

func (c *Clientset) Tracker() tridenttesting.ObjectTracker {
	return c.tracker
}

var _ versioned.Interface = &Clientset{}

// TridentV1 retrieves the TridentV1Client
func (c *Clientset) TridentV1() v1.TridentV1Interface {
	return &v1fake.FakeTridentV1{Fake: &c.Fake}
}
